package semant;

import translate.Level;
import util.BoolList;

public class Semant {
	Env env;

	types.Type INT;
	types.Type NIL;
	types.Type STRING;
	types.VOID VOID;
	types.BLANK BLANK;
	
	boolean leftValue = false;
	boolean subValue;
	// Level currentLev = new Level(null, null, null);
	translate.Translate trans;

	public Semant(errorMsg.ErrorMsg err) {
		this(new Env(err));
	}

	Semant(Env e) {
		trans = new translate.Translate();
		env = e;
		INT = new types.INT();
		NIL = new types.NIL();
		STRING = new types.STRING();
		BLANK = new types.BLANK();
		VOID = new types.VOID();
		env.tenv.put(symbol.Symbol.symbol("int"), INT);
		env.tenv.put(symbol.Symbol.symbol("string"), STRING);
		env.venv.put(symbol.Symbol.symbol("print"), new FunEntry(new translate.ExtCallLevel(trans.currentLev, symbol.Symbol.symbol("print")),
				new temp.Label("print"), new types.RECORD(symbol.Symbol
						.symbol("s"), STRING, null), VOID));
		env.venv.put(symbol.Symbol.symbol("printi"), new FunEntry(new translate.ExtCallLevel(trans.currentLev, symbol.Symbol.symbol("print")),
				new temp.Label("printi"), new types.RECORD(symbol.Symbol
						.symbol("i"), INT, null), VOID));
		env.venv.put(symbol.Symbol.symbol("flush"), new FunEntry(new translate.ExtCallLevel(trans.currentLev, symbol.Symbol.symbol("print")),
				new temp.Label("flush"), BLANK, VOID));
		env.venv.put(symbol.Symbol.symbol("getchar"), new FunEntry(new translate.ExtCallLevel(trans.currentLev, symbol.Symbol.symbol("print")),
				new temp.Label("getchar"), BLANK, STRING));
		env.venv.put(symbol.Symbol.symbol("ord"), new FunEntry(new translate.ExtCallLevel(trans.currentLev, symbol.Symbol.symbol("print")),
				new temp.Label("ord"), new types.RECORD(symbol.Symbol
						.symbol("s"), STRING, null), INT));
		env.venv.put(symbol.Symbol.symbol("chr"), new FunEntry(new translate.ExtCallLevel(trans.currentLev, symbol.Symbol.symbol("print")),
				new temp.Label("chr"), new types.RECORD(symbol.Symbol
						.symbol("i"), INT, null), STRING));
		env.venv.put(symbol.Symbol.symbol("size"), new FunEntry(new translate.ExtCallLevel(trans.currentLev, symbol.Symbol.symbol("print")),
				new temp.Label("size"), new types.RECORD(symbol.Symbol
						.symbol("s"), STRING, null), INT));
		env.venv.put(symbol.Symbol.symbol("substring"), new FunEntry(new translate.ExtCallLevel(trans.currentLev, symbol.Symbol.symbol("print")),
				new temp.Label("substring"), new types.RECORD(symbol.Symbol
						.symbol("s"), STRING, new types.RECORD(symbol.Symbol
						.symbol("f"), INT, new types.RECORD(symbol.Symbol
						.symbol("n"), INT, null))), STRING));
		env.venv.put(symbol.Symbol.symbol("concat"), new FunEntry(new translate.ExtCallLevel(trans.currentLev, symbol.Symbol.symbol("print")),
				new temp.Label("concat"), new types.RECORD(symbol.Symbol
						.symbol("s1"), STRING, new types.RECORD(symbol.Symbol
						.symbol("s2"), STRING, null)), STRING));
		env.venv.put(symbol.Symbol.symbol("not"), new FunEntry(new translate.ExtCallLevel(trans.currentLev, symbol.Symbol.symbol("print")),
				new temp.Label("not"), new types.RECORD(symbol.Symbol
						.symbol("i"), INT, null), INT));
		env.venv.put(symbol.Symbol.symbol("exit"), new FunEntry(new translate.ExtCallLevel(trans.currentLev, symbol.Symbol.symbol("print")),
				new temp.Label("exit"), new types.RECORD(symbol.Symbol
						.symbol("i"), INT, null), VOID));

	}

	void error(int pos, String message) {
		env.errorMsg.error(pos, message);
	}

	void checkInt(types.Type a, int pos) {
		if (!(a instanceof types.INT))
			error(pos, "integer required");
	}

	void checkString(types.Type a, int pos) {
		if (!(a instanceof types.STRING))
			error(pos, "string required");
	}

	void checkStringInt(types.Type a, int pos) {
		if (!(a instanceof types.STRING || a instanceof types.INT))
			error(pos, "integer or string required");
	}

	void checkNotVoid(types.Type a, int pos) {
		if (a instanceof types.VOID)
			error(pos, "valued type required");
	}

	void checkVoid(types.Type a, int pos) {
		if (!(a instanceof types.VOID))
			error(pos, "void type required");
	}

	void checkConsistency(types.Type a, types.Type b, int pos) {
		types.Type acta = a.actual();
		types.Type actb = b.actual();

		if (acta == NIL && actb == NIL)
			error(pos, "cannot compare two nil types");
		else if (acta == NIL && actb instanceof types.RECORD)
			return;
		else if (acta instanceof types.RECORD && actb == NIL)
			return;
		else if (acta != actb)
			error(pos, "Type not consistent");
	}

	public translate.Frag transProgram(absyn.Exp e) {
		ExpTy x = transExp(e);

		if (x.ty == NIL)
			error(e.pos, "a program cannot be of nil type");
		trans.procEntryExit(trans.currentLev, x.exp, false);
		return trans.getResult();
	}

	ExpTy transExp(absyn.Exp e) {
		if (e instanceof absyn.VarExp)
			return transExp((absyn.VarExp) e);
		if (e instanceof absyn.NilExp)
			return transExp((absyn.NilExp) e);
		if (e instanceof absyn.IntExp)
			return transExp((absyn.IntExp) e);
		if (e instanceof absyn.StringExp)
			return transExp((absyn.StringExp) e);
		if (e instanceof absyn.CallExp)
			return transExp((absyn.CallExp) e);
		if (e instanceof absyn.OpExp)
			return transExp((absyn.OpExp) e);
		if (e instanceof absyn.RecordExp)
			return transExp((absyn.RecordExp) e);
		if (e instanceof absyn.SeqExp)
			return transExp((absyn.SeqExp) e);
		if (e instanceof absyn.AssignExp)
			return transExp((absyn.AssignExp) e);
		if (e instanceof absyn.IfExp)
			return transExp((absyn.IfExp) e);
		if (e instanceof absyn.WhileExp)
			return transExp((absyn.WhileExp) e);
		if (e instanceof absyn.ForExp)
			return transExp((absyn.ForExp) e);
		if (e instanceof absyn.BreakExp)
			return transExp((absyn.BreakExp) e);
		if (e instanceof absyn.LetExp)
			return transExp((absyn.LetExp) e);
		if (e instanceof absyn.ArrayExp)
			return transExp((absyn.ArrayExp) e);
		throw new Error("transExp");
	}

	ExpTy transExp(absyn.VarExp e) {
		ExpTy et = transVar(e.var);
		return new ExpTy(new translate.VarExp(et.exp), et.ty);
	}

	ExpTy transExp(absyn.NilExp e) {
		return new ExpTy(new translate.NilExp(), NIL);
	}

	ExpTy transExp(absyn.IntExp e) {
		return new ExpTy(new translate.IntExp(e.value), INT);
	}

	ExpTy transExp(absyn.StringExp e) {
		translate.StringExp sexp = new translate.StringExp(e.value);
		trans.addDataFrag(trans.currentLev, sexp.label , e.value);
		return new ExpTy(sexp, STRING);
	}

	ExpTy transExp(absyn.CallExp e) {
		Object x;
		translate.CallExp ce = null;

		x = env.venv.get(e.func);
		if (!(x instanceof FunEntry)) {
			error(e.pos, "function undefined");
			return new ExpTy(new translate.IntExp(0), VOID);
		}

		FunEntry fe = (FunEntry) x;
		absyn.ExpList fl = e.args;
		types.RECORD fp = fe.formals;
		ExpTy temp;

		ce = new translate.CallExp(fe.label, fe.level);
		if (fl == null && !(fe.formals instanceof types.BLANK))
			error(e.pos, "require arguments");
		else if (fl != null && fe.formals instanceof types.BLANK)
			error(e.args.head.pos, "redundant argument(s)");
		else if (!(fl == null && fe.formals instanceof types.BLANK)) {
			while (fl != null && fp != null) {
				temp = transExp(fl.head);
				checkConsistency(temp.ty, fp.fieldType, e.args.head.pos);
				ce.addArg(temp.exp);
				fl = fl.tail;
				fp = fp.tail;
			}
			if (!(fl == null && fp == null))
				error(e.args.head.pos, "count of arguments mismatched");
		}
		ce.calcStaticLink(trans.currentLev, fe.level);
		return new ExpTy(ce, fe.result);
	}

	ExpTy transExp(absyn.OpExp e) {
		ExpTy tl = transExp(e.left);
		ExpTy tr = transExp(e.right);

		switch (e.oper) {
		case absyn.OpExp.PLUS:
			checkInt(tl.ty, e.left.pos);
			checkInt(tr.ty, e.right.pos);
			return new ExpTy(new translate.OpExp(tl.exp, tr.exp,
					translate.OpRelCode.PLUS), INT);
		case absyn.OpExp.MINUS:
			checkInt(tl.ty, e.left.pos);
			checkInt(tr.ty, e.right.pos);
			return new ExpTy(new translate.OpExp(tl.exp, tr.exp,
					translate.OpRelCode.MINUS), INT);
		case absyn.OpExp.MUL:
			checkInt(tl.ty, e.left.pos);
			checkInt(tr.ty, e.right.pos);
			return new ExpTy(new translate.OpExp(tl.exp, tr.exp,
					translate.OpRelCode.MUL), INT);
		case absyn.OpExp.DIV:
			checkInt(tl.ty, e.left.pos);
			checkInt(tr.ty, e.right.pos);
			return new ExpTy(new translate.OpExp(tl.exp, tr.exp,
					translate.OpRelCode.DIV), INT);
		case absyn.OpExp.LT:
			checkStringInt(tl.ty, e.left.pos);
			checkStringInt(tr.ty, e.right.pos);
			checkConsistency(tl.ty, tr.ty, e.left.pos);
			if (tl.ty instanceof types.STRING && tr.ty instanceof types.STRING)
				return new ExpTy(new translate.StringValueCompExp(trans.currentLev, tl.exp, tr.exp, 0), INT);
			return new ExpTy(new translate.RelCx(tl.exp, tr.exp,
					translate.OpRelCode.LT), INT);
		case absyn.OpExp.LE:
			checkStringInt(tl.ty, e.left.pos);
			checkStringInt(tr.ty, e.right.pos);
			checkConsistency(tl.ty, tr.ty, e.left.pos);
			if (tl.ty instanceof types.STRING && tr.ty instanceof types.STRING)
				return new ExpTy(new translate.StringValueCompExp(trans.currentLev, tl.exp, tr.exp, 2), INT);
			return new ExpTy(new translate.RelCx(tl.exp, tr.exp,
					translate.OpRelCode.LE), INT);
		case absyn.OpExp.GT:
			checkStringInt(tl.ty, e.left.pos);
			checkStringInt(tr.ty, e.right.pos);
			checkConsistency(tl.ty, tr.ty, e.left.pos);
			if (tl.ty instanceof types.STRING && tr.ty instanceof types.STRING)
				return new ExpTy(new translate.StringValueCompExp(trans.currentLev, tl.exp, tr.exp, 1), INT);
			return new ExpTy(new translate.RelCx(tl.exp, tr.exp,
					translate.OpRelCode.GT), INT);
		case absyn.OpExp.GE:
			checkStringInt(tl.ty, e.left.pos);
			checkStringInt(tr.ty, e.right.pos);
			checkConsistency(tl.ty, tr.ty, e.left.pos);
			if (tl.ty instanceof types.STRING && tr.ty instanceof types.STRING)
				return new ExpTy(new translate.StringValueCompExp(trans.currentLev, tl.exp, tr.exp, 3), INT);
			return new ExpTy(new translate.RelCx(tl.exp, tr.exp,
					translate.OpRelCode.GE), INT);
		case absyn.OpExp.EQ:
			checkNotVoid(tl.ty, e.left.pos);
			checkNotVoid(tr.ty, e.right.pos);
			checkConsistency(tl.ty, tr.ty, e.left.pos);
			if (tl.ty instanceof types.STRING && tr.ty instanceof types.STRING)
				return new ExpTy(new translate.StringCompExp(trans.currentLev, tl.exp, tr.exp, 1), INT);
			return new ExpTy(new translate.RelCx(tl.exp, tr.exp,
					translate.OpRelCode.EQ), INT);
		case absyn.OpExp.NE:
			checkNotVoid(tl.ty, e.left.pos);
			checkNotVoid(tr.ty, e.right.pos);
			checkConsistency(tl.ty, tr.ty, e.left.pos);
			if (tl.ty instanceof types.STRING && tr.ty instanceof types.STRING)
				return new ExpTy(new translate.StringCompExp(trans.currentLev, tl.exp, tr.exp, 0), INT);
			return new ExpTy(new translate.RelCx(tl.exp, tr.exp,
					translate.OpRelCode.NE), INT);
		}

		return null; // temp code
	}

	ExpTy transExp(absyn.RecordExp e) {
		absyn.FieldExpList fel = e.fields;
		translate.RecordExp re = new translate.RecordExp(trans.currentLev);
		types.Type rt = (types.NAME) env.tenv.get(e.typ);
		types.Type rp;
		ExpTy temp;

		if (rt == null) {
			error(e.pos, "undefined type");
			return new ExpTy(re, INT);
		} else {
			rp = rt.actual();
			if (!(rp instanceof types.RECORD))
				error(e.pos, "record type needed");
			else if (fel == null) {
				if (!(rp instanceof types.BLANK))
					error(e.pos, "record fields not consistent");
			} else {
				while (!(rp instanceof types.BLANK) && fel != null
						&& rp != null) {
					temp = transExp(fel.init);
					if (fel.name != ((types.RECORD) rp).fieldName)
						error(e.pos, "record fields not consistent");
					checkConsistency(temp.ty, ((types.RECORD) rp).fieldType,
							fel.init.pos);
					re.addField(temp.exp);
					fel = fel.tail;
					rp = ((types.RECORD) rp).tail;
				}
				if (!(fel == null && rp == null))
					error(e.pos,
							"record fields not consistent (number of fields differs)");
			}
			return new ExpTy(re, rt);
		}
	}

	ExpTy transExp(absyn.SeqExp e) {
		absyn.ExpList el = e.list;
		translate.SeqExp se = new translate.SeqExp();
		ExpTy temp = null;

		while (el != null) {
			temp = transExp(el.head);
			if (el.tail != null && temp.ty == NIL)
				error(el.head.pos,
						"nil type expression cannot be used without context determining its type");
			el = el.tail;
			se.addStm(temp.exp);
		}
		if (e.list != null)
			return new ExpTy(se, temp.ty);
		else
			return new ExpTy(new translate.IntExp(0), VOID);
	}

	ExpTy transExp(absyn.AssignExp e) {
		boolean prev = leftValue;
		
		if (e.var instanceof absyn.SimpleVar) leftValue = true;
		ExpTy vt = transVar(e.var);
		leftValue = prev;
		ExpTy et = transExp(e.exp);

		checkConsistency(vt.ty, et.ty, e.pos);
		return new ExpTy(new translate.AssignExp(vt.exp, et.exp), VOID);
	}

	ExpTy transExp(absyn.IfExp e) {
		translate.Exp trE = null;
		ExpTy tet = transExp(e.test);
		ExpTy tct = transExp(e.thenclause);
		ExpTy elt;

		checkInt(tet.ty, e.pos);
		if (e.elseclause != null) {
			elt = transExp(e.elseclause);
			if (elt.ty != NIL || tct.ty != NIL)
				checkConsistency(tct.ty, elt.ty, e.thenclause.pos);
			trE = new translate.IfThenElseExp(tet.exp, tct.exp, elt.exp);
		} else {
			elt = new ExpTy(null, VOID);
			checkVoid(tct.ty, e.thenclause.pos);
			trE = new translate.IfThenExp(tet.exp, tct.exp);
		}
		if (tct.ty == NIL)
			return new ExpTy(trE, elt.ty);
		else
			return new ExpTy(trE, tct.ty);
	}

	ExpTy transExp(absyn.WhileExp e) {
		ExpTy et = transExp(e.test);

		checkInt(et.ty, e.test.pos);

		env.loopEnd.push(new temp.Label());
		env.loopCnt.beginLoop();
		ExpTy eb = transExp(e.body);
		env.loopCnt.endLoop();

		checkVoid(eb.ty, e.body.pos);
		return new ExpTy(new translate.WhileExp(et.exp, eb.exp, env.loopEnd.pop()), VOID);
	}

	ExpTy transExp(absyn.ForExp e) {
		ExpTy ht = transExp(e.hi);
		ExpTy bte;
		translate.Exp decExp;

		env.venv.beginScope();
		env.tenv.beginScope();
		// ExpTy vt = transExp(transDec(e.var, true));
		decExp = transDec(e.var, true);
		checkInt(ht.ty, e.hi.pos);

		env.loopCnt.beginLoop();
		env.loopEnd.add(new temp.Label());
		bte = transExp(e.body);
		checkVoid(bte.ty, e.body.pos);
		temp.Label l = env.loopEnd.pop();
		VarEntry ve = (VarEntry) env.venv.get(e.var.name);
		env.loopCnt.endLoop();

		env.venv.endScope();
		env.tenv.endScope();
		return new ExpTy(new translate.ForExp(decExp, ht.exp, bte.exp,
				ve.access, l), VOID);
	}

	ExpTy transExp(absyn.BreakExp e) {
		if (!env.loopCnt.isInLoop()) {
			error(e.pos, "break must be in for");
			return new ExpTy(new translate.IntExp(0), VOID);
		}
		else
			return new ExpTy(new translate.BreakExp(env.loopEnd.peek()), VOID);
	}

	ExpTy transExp(absyn.LetExp e) {
		translate.LetExp le = new translate.LetExp();

		env.venv.beginScope();
		env.tenv.beginScope();

		for (absyn.DecList p = e.decs; p != null; p = p.tail) {
			translate.Exp txp = transDec(p.head);
			if (txp != null) le.addDec(txp);
		}

		ExpTy et = transExp(e.body);
		le.setBody(et.exp);

		env.venv.endScope();
		env.tenv.endScope();
		return new ExpTy(le, et.ty);
	}

	ExpTy transExp(absyn.ArrayExp e) {
		Object te = env.tenv.get(e.typ);

		if (te == null) {
			error(e.pos, "type undefined");
			return new ExpTy(new translate.IntExp(0), INT);
		} else {
			ExpTy it = transExp(e.init), st = transExp(e.size);
			checkConsistency(
					((types.ARRAY) ((types.Type) te).actual()).element, it.ty,
					e.init.pos);
			return new ExpTy(new translate.ArrayExp(st.exp, it.exp,
					trans.currentLev), (types.Type) te);
		}
	}

	ExpTy transVar(absyn.Var v) {
		if (v instanceof absyn.SimpleVar)
			return transVar((absyn.SimpleVar) v);
		if (v instanceof absyn.FieldVar)
			return transVar((absyn.FieldVar) v);
		if (v instanceof absyn.SubscriptVar)
			return transVar((absyn.SubscriptVar) v);
		throw new Error("transVar");
	}

	ExpTy transVar(absyn.SimpleVar v) {
		// Symbol.Symbol y = (Symbol.Symbol)iterVar.get(v.name);
		Entry x = (Entry) env.venv.get(v.name);

		if (x instanceof VarEntry && !(leftValue && ((VarEntry) x).isItrVar())) {
			VarEntry ent = (VarEntry) x;
			return new ExpTy(trans.simpleVar(ent.access), ent.ty);
		} else {
			if (x == null)
				error(v.pos, "variable undefined");
			else if (!(x instanceof VarEntry))
				error(v.pos, "variable needed");
			else if (leftValue && ((VarEntry) x).isItrVar())
				error(v.pos, "iteration variable cannot be assigned to");
			else
				error(v.pos, "undefined variable");
			return new ExpTy(new translate.IntExp(0), INT);
		}
	}

	ExpTy transVar(absyn.SubscriptVar v) {
		ExpTy vt = transVar(v.var);
		ExpTy it = transExp(v.index);

		if (!(vt.ty.actual() instanceof types.ARRAY)) {
			error(v.pos, "subscript require array type");
			return new ExpTy(new translate.IntExp(0), BLANK);
		} else
			return new ExpTy(trans.subscriptVar(it.exp, vt.exp),
					((types.ARRAY) vt.ty.actual()).element);
	}

	ExpTy transVar(absyn.FieldVar v) {
		ExpTy vt = transVar(v.var);
		types.RECORD rt;
		int offset = 0;

		if (vt.ty.actual() instanceof types.RECORD) {
			rt = (types.RECORD) vt.ty.actual();
			while (rt != null && rt.fieldName != v.field) {
				rt = rt.tail;
				offset++;
			}
			if (rt != null) {
				return new ExpTy(trans.fieldVar(offset, vt.exp), rt.fieldType);
			} else {
				error(v.pos, "inconsistent fieldname");
				return new ExpTy(new translate.IntExp(0), BLANK);
			}
		} else {
			error(v.pos, "record-type required");
			return new ExpTy(new translate.IntExp(0), INT);
		}
	}

	translate.Exp transDec(absyn.Dec d) {
		if (d instanceof absyn.FunctionDec)
			return transDec((absyn.FunctionDec) d);
		if (d instanceof absyn.VarDec)
			return transDec((absyn.VarDec) d);
		if (d instanceof absyn.TypeDec)
			return transDec((absyn.TypeDec) d);
		error(d.pos, "transDec");
		return null;
	}

	translate.Exp transDec(absyn.VarDec d) {
		return transDec(d, false);
	}

	translate.Exp transDec(absyn.VarDec d, boolean iterVar) {
		types.Type result;
		ExpTy et = transExp(d.init);
		translate.Access ta = null;

		if (d.typ == null) {
			if (et.ty == NIL)
				error(d.init.pos,
						"a variable cannot be assigned to by a nil without knowing its type");
			if (et.ty == VOID)
				error(d.init.pos,
						"a variable cannot be assigned to by void type");
			env.venv.put(d.name, new VarEntry(ta = trans.currentLev
					.allocLocal(d.escape), et.ty, iterVar));
		} else {
			result = transTy(d.typ);
			checkConsistency(result, et.ty, d.pos);
			env.venv.put(d.name, new VarEntry(ta = trans.currentLev
					.allocLocal(d.escape), result, iterVar));
		}
		if (iterVar)
			checkInt(et.ty, d.pos);
		return new translate.AssignExp(trans.simpleVar(ta), et.exp);
	}

	translate.Exp transDec(absyn.FunctionDec d) {
		BoolList bl, tail = null;
		absyn.FieldList p;
		absyn.FunctionDec dp = d;
		ExpTy bodyresult;

		env.loopCnt.beginFunction();
		java.util.Dictionary<symbol.Symbol, symbol.Symbol> dict = new java.util.Hashtable<symbol.Symbol, symbol.Symbol>();
		while (dp != null) {
			bl = null;
			for (absyn.FieldList tfl = dp.params; tfl != null; tfl = tfl.tail)
				if (bl == null)
					tail = bl = new BoolList(tfl.escape, null);
				else {
					tail.tail = new BoolList(tfl.escape, null);
					tail = tail.tail;
				}
			types.Type result = transTy((absyn.Ty) dp.result);
			types.RECORD formals = transTypeFields(dp.params, false);
			Object x = dict.get(dp.name);
			if (x != null)
				error(dp.pos,
						"a function with the same name has already declared");
			dict.put(dp.name, dp.name);
			Level nl = new Level(trans.currentLev, d.name, bl);
			env.venv.put(dp.name, new FunEntry(nl, nl.getFunLabel(), formals, result));
			dp = dp.next;
		}

		dp = d;
		while (dp != null) {
			env.venv.beginScope();
			FunEntry funEn = (FunEntry) env.venv.get(dp.name);
			Level lev = funEn.level;
			trans.beginLevel(lev);
			translate.AccessList a = lev.formals;
			for (p = dp.params; p != null; p = p.tail) {
				if (env.tenv.get(p.typ) == null)
					error(p.pos, "undefined type");
				else
					env.venv.put(p.name, new VarEntry(a.head, 
							(types.Type) env.tenv.get(p.typ)));
				a = a.tail;
			}
			bodyresult = transExp(dp.body);
			trans.procEntryExit(trans.currentLev, bodyresult.exp, bodyresult.ty != VOID);
			env.venv.endScope();
			trans.endLevel();
			checkConsistency(bodyresult.ty,
					((FunEntry) env.venv.get(dp.name)).result, dp.body.pos);
			dp = dp.next;
		}
		env.loopCnt.endFunction();
		return null;
	}

	translate.Exp transDec(absyn.TypeDec d) {
		absyn.TypeDec tp = d;
		types.NAME x;

		java.util.Dictionary<symbol.Symbol, symbol.Symbol> dict = new java.util.Hashtable<symbol.Symbol, symbol.Symbol>();
		while (tp != null) {
			Object o = dict.get(tp.name);
			if (o != null)
				error(tp.pos,
						"another type with the same name has already defined");
			env.tenv.put(tp.name, new types.NAME(tp.name));
			dict.put(tp.name, tp.name);
			tp = tp.next;
		}
		tp = d;
		while (tp != null) {
			types.Type result = transTy(tp.ty);
			x = (types.NAME) env.tenv.get(tp.name);
			x.bind(result);
			tp = tp.next;
		}
		tp = d;
		while (tp != null) {
			x = (types.NAME) env.tenv.get(tp.name);
			if (x.isLoop())
				error(tp.pos, "illegal mutually recursive type definitions");
			tp = tp.next;
		}
		return null;
	}

	types.RECORD transTypeFields(absyn.FieldList f) {
		return transTypeFields(f, true);
	}

	types.RECORD transTypeFields(absyn.FieldList f, boolean judgeRep) {
		types.RECORD head, tail;
		types.Type temp;
		absyn.FieldList p = f;
		java.util.Dictionary<symbol.Symbol, symbol.Symbol> dict = new java.util.Hashtable<symbol.Symbol, symbol.Symbol>();

		if (f == null) {
			return new types.BLANK();
		} else {
			temp = (types.Type) env.tenv.get(p.typ);
			head = new types.RECORD(p.name, temp, null);
			dict.put(p.name, p.name);
			tail = head;
			p = p.tail;
			while (p != null) {
				if (!judgeRep || dict.get(p.name) == null) {
					temp = (types.Type) env.tenv.get(p.typ);
					if (temp == null)
						error(p.pos, "undefined type");
					tail.tail = new types.RECORD(p.name, temp, null);
					tail = tail.tail;
				} else
					error(p.pos, "different fieldnames needed");
				p = p.tail;
			}
			return head;
		}
	}

	types.Type transTy(absyn.Ty t) {
		if (t == null)
			return VOID;
		if (t instanceof absyn.NameTy)
			return transTy((absyn.NameTy) t);
		if (t instanceof absyn.RecordTy)
			return transTy((absyn.RecordTy) t);
		if (t instanceof absyn.ArrayTy)
			return transTy((absyn.ArrayTy) t);
		error(t.pos, "transTy");
		return null;
	}

	types.Type transTy(absyn.NameTy t) {
		types.Type x = (types.Type) env.tenv.get(t.name);
		if (x == null) {
			error(t.pos, "undefined type");
			return INT;
		}
		return x;
	}

	types.Type transTy(absyn.ArrayTy t) {
		return new types.ARRAY((types.Type) env.tenv.get(t.typ));
	}

	types.Type transTy(absyn.RecordTy t) {
		return transTypeFields(t.fields);
	}
}
